package org.xfy.ymcc.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.messaging.support.MessageBuilder;
import org.xfy.common.constant.KillConst;
import org.xfy.common.exception.GlobalException;
import org.xfy.common.jwt.JwtUtils;
import org.xfy.common.result.JsonResult;
import org.xfy.common.util.AssertUtil;
import org.xfy.common.util.CodeGenerateUtils;
import org.xfy.ymcc.domain.KillActivity;
import org.xfy.ymcc.domain.KillCourse;
import org.xfy.ymcc.domain.Login;
import org.xfy.ymcc.dto.KillDto;
import org.xfy.ymcc.dto.PreOrderDto;
import org.xfy.ymcc.mapper.KillActivityMapper;
import org.xfy.ymcc.service.IKillActivityService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author xfy
 * @since 2023-12-12
 */
@Service
public class KillActivityServiceImpl extends ServiceImpl<KillActivityMapper, KillActivity> implements IKillActivityService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    @Override
    public JsonResult kill(KillDto killDto, HttpServletRequest request) {
        //判断用户是否购买过
        String header = request.getHeader("U-TOKEN");
        JSONObject userInfo = (JSONObject) JwtUtils.getUserInfo(header);
        Login parse = JSON.parseObject(JSON.toJSONString(userInfo.get("data")), Login.class);
        //从redis里面去取出数据
        BoundHashOperations<String, Object, Object> hashOps = redisTemplate.boundHashOps(KillConst.KEY_KILL_COURSE);
        KillCourse killCourse = (KillCourse) hashOps.get(killDto.getShopID().toString());
        Boolean b = redisTemplate.hasKey(KillConst.KILL_RECORD + "_" + parse.getId() + "_" + killCourse.getId());
        AssertUtil.isFalse(b,"请勿重复秒杀");
        //判断是否还在秒杀活动内
        Date date = new Date();
        AssertUtil.isTrue(date.after(killCourse.getStartTime()) && date.before(killCourse.getEndTime()), "活动已结束");
        //判断是否多买了
        AssertUtil.isTrue(killCourse.getKillLimit() >= killDto.getNumber(),
                "每人限购" + killCourse.getKillLimit() + "份！");
        String orderNo = null;
        //2. Redisson分布式锁：信号量减库存  库存预热的key
        RSemaphore semaphore = redissonClient.getSemaphore(KillConst.KILL_STORE + ":" + killCourse.getId());
        //分布式锁：尝试抢锁（尝试扣减库存，减本次秒杀的数量）
        //不传后面的数量默认减1
        boolean tryAcquire = semaphore.tryAcquire(killDto.getNumber());
        if (tryAcquire) {
            //伪下单
            //抢到了锁，扣减库存也成功了：
            //3. 创建订单号，准备“预创订单数据”
            orderNo = CodeGenerateUtils.generateOrderSn(parse.getId());
            //4. 保存“预创订单数据到Redis”   PreOrderDto：需要什么字段？依据是下订单需要哪些字段 就设置哪些字段
            PreOrderDto preOrderDto = new PreOrderDto();
            preOrderDto.setOrderNo(orderNo);
            preOrderDto.setUserId(parse.getId());
            preOrderDto.setTitle("秒杀订单");
            preOrderDto.setOrderType(1);
            preOrderDto.setPrice(killCourse.getKillPrice());
            preOrderDto.setCount(killDto.getNumber());
            preOrderDto.setCourseId(killCourse.getCourseId());
            preOrderDto.setCourseName(killCourse.getCourseName());
            preOrderDto.setCoursePic(killCourse.getCoursePic());
            preOrderDto.setKillCourseId(killCourse.getId());
            redisTemplate.opsForValue().set(KillConst.KEY_PREFIX_PRE_ORDER + ":" + orderNo, preOrderDto);

            //5. 发送延迟消息到MQ，用作下单超时处理（5分钟）：【消费者写在订单服务中】5分钟后用户仍未下单（通过订单编号去查询订单表），则取消资格【通过订单编号为key删除redis中保存的预创订单】
            rocketMQTemplate.syncSend(
                    KillConst.TOPIC_ORDER_TIMEOUT + ":" + KillConst.TAGS_ORDER_TIMEOUT,
                    MessageBuilder.withPayload(orderNo).build(),
                    3000, 5
            );

            //6. 保存用户秒杀记录到Redis防止重复秒杀  KILL_RECORD_用户id_秒杀课程id =》 value随意
            redisTemplate.opsForValue().set(KillConst.KILL_RECORD + "_" + parse.getId() + "_" + killCourse.getId(), 1);
        } else {
            throw new GlobalException("秒杀失败！");
        }
        //7. 返回订单号给前端，用作下单依据
        return JsonResult.success().setData(orderNo);
    }
}
